package springboot.kernel;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.opentcs.access.Kernel;
import org.opentcs.access.KernelServicePortal;
import org.opentcs.access.rmi.KernelServicePortalBuilder;
import org.opentcs.access.to.order.DestinationCreationTO;
import org.opentcs.access.to.order.TransportOrderCreationTO;
import org.opentcs.components.kernel.services.DispatcherService;
import org.opentcs.components.kernel.services.TransportOrderService;
import org.opentcs.components.kernel.services.VehicleService;
import org.opentcs.data.TCSObjectReference;
import org.opentcs.data.model.Vehicle;
import org.opentcs.data.order.DriveOrder;
import org.opentcs.data.order.TransportOrder;
import org.springframework.stereotype.Component;
import springboot.bean.TaskState;

import java.util.*;

@Slf4j
@Component
@NoArgsConstructor
public class KernelTools {

    private KernelServicePortal kernel=null;
    private boolean connect=false;
    private boolean kernel_connect_exception=false;
    private Timer dogCheckTimer = null;
    private boolean kernelCloseManual=false;

    private void setConnect(boolean connect){
        this.connect=connect;
    }

    /**
     * connect to kernel and watch this state only connect once
     */
    public void getConnect(){
        if (!this.connect && kernel==null){
            connect();
            timerCheckKernel();
        }else {
            log.info("kernel is login,don't login again");
        }
    }

    /**
     * close connect to kernel
     */
    public void getClose(){
        close();
    }

    /**
     * get order state which is ready to load
     * @param vehicleName
     * @param orderName
     * @return
     */
   /* public boolean getReadyToLoad(String vehicleName,String orderName){
        try {
            TaskState taskState = getTaskState(vehicleName,orderName);
            if (taskState != null){
                if (taskState.getOperation().startsWith("Load") &&
                        taskState.getStateName().equals("TRAVELLING") &&
                        !taskState.getFinalDestinationPoint().equals(taskState.getCurrentPosition())){
                    return true;
                }
            }
        }catch (Exception e){
            log.info("error is at getReadyToLoad");
            e.printStackTrace();
        }
        return false;
    }*/

    /**
     * get order state which is ready to unload
     * @param vehicleName
     * @param orderName
     * @return
     */
    public boolean getReadyToUnload(String vehicleName,String orderName){
        try {
            TaskState taskState = getTaskState(vehicleName,orderName);
            if (taskState.getOperation().startsWith("Unload") &&
                    taskState.getStateName().equals("TRAVELLING") &&
                    !taskState.getFinalDestinationPoint().equals(taskState.getCurrentPosition())){
                return true;
            }
        }catch (Exception e){
            log.info("error is at getReadyToUnload");
            e.printStackTrace();
        }
        return false;
    }

    /**
     * get order state which is finished
     * @param orderName
     * @return
     */
    public boolean getFinished(String orderName){
            String finalState = getFinalState(orderName);
            if (finalState.equals("FINISHED")){
                return true;
            }else {
                return false;
            }
    }

    /**
     * get order state which is failed
     * @param orderName
     * @return
     */
    public boolean getFailed(String orderName){
        String finalState = getFinalState(orderName);
        if (finalState.equals("FAILED")){
            return true;
        }else {
            return false;
        }
    }

    /**
     * get agv has or not has task
     * @param vehicleName
     * @return
     */
    public boolean getAgvHasTask(String vehicleName){
        if (getKernel()!=null && this.connect){
            VehicleService vehicleService = kernel.getVehicleService();
            Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, vehicleName);
            TCSObjectReference<TransportOrder> transportOrder = vehicle.getTransportOrder();
            if (transportOrder==null){
                return false;
            }else {
                String name = transportOrder.getName();
                if (name==null){
                    return false;
                }else if (name.startsWith("Park") || name.startsWith("Recharge") || name.startsWith("TOrder") || name.startsWith("Move")){
                    return false;
                }else{
                    return true;
                }
            }

        }
        return false;
    }

    /**
     * get agv executing orderName
     * @param vehicleName
     * @return
     */
    public String  getOrderName(String vehicleName){
        if (getKernel()!=null && this.connect) {
            VehicleService vehicleService = kernel.getVehicleService();
            Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, vehicleName);
            if (vehicle.getTransportOrder() != null){
                return vehicle.getTransportOrder().getName();
            }
        }
        return "NO_TASK";
    }
    private String getFinalState(String orderName){
        boolean isFinalState = false;
        try {
            if(orderName != null){
                if (getKernel()!=null && this.connect){
                    TransportOrderService orderService = kernel.getTransportOrderService();

                    TransportOrder TO= orderService.fetchObject(TransportOrder.class, orderName);
                    TransportOrder.State state = TO.getState();
                    if (state != null){
                        isFinalState = TO.getState().isFinalState();
                    }
                    if(isFinalState){
                        String name = TO.getState().name();
                        if (name.equals("FINISHED")){
                            return "FINISHED";
                        }else if (name.equals("FAILED")){
                            return "FAILED";
                        }
                    }
                }
            }
        }catch (Exception e){
            log.info("error is at getFinalState");
            e.printStackTrace();
        }

        return "NO_END";
    }

    private TaskState getTaskState(String vehicleName,String orderName){
        try {
            if(getKernel()!=null && this.connect){
                VehicleService vehicleService = kernel.getVehicleService();
                Vehicle vehicle = vehicleService.fetchObject(Vehicle.class, vehicleName);
                String currentPosition = vehicle.getCurrentPosition().getName();
                //获得任务相关
                TransportOrderService orderService = kernel.getTransportOrderService();
                TransportOrder TO= orderService.fetchObject(TransportOrder.class, orderName);
                DriveOrder currentDriveOrder = TO.getCurrentDriveOrder();
                String operation = currentDriveOrder.getDestination().getOperation();
                String stateName = currentDriveOrder.getState().name();
                String finalDestinationPoint = currentDriveOrder.getRoute().getFinalDestinationPoint().getName();
                return new TaskState(operation,stateName,finalDestinationPoint,currentPosition);
            }
        }catch (Exception e){
            log.info("error is at getTaskState");
            e.printStackTrace();
        }
        return null;
    }

    private void close(){
        if (this.connect && kernel!=null){
            setKernelManualClose(true);
            kernel.logout();
            kernel=null;
            setConnect(false);
        }else {
            log.info("kernel is not connect,don't logout again");
        }
    }

    private void connect(){
        try {
            kernel =new KernelServicePortalBuilder().build();
            kernel.login("localhost",1099);
            kernel_connect_exception=false;
        }catch (Exception e){
            log.info("kernel connect exception");
            setConnect(false);
            kernel_connect_exception=true;
        }
        if (!kernel_connect_exception){//kernel connected
            setConnect(true);
            setKernelManualClose(false);
            log.info("kernel is login");
        }

    }

    private void timerCheckKernel() {
        //check kernel state period 5s
        if(dogCheckTimer==null) {
            dogCheckTimer = new Timer();
        }
        dogCheckTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                if(getKernelManualClose() || getKernel()==null){
                    setConnect(false);
                    return;
                }
                try{
                    Kernel.State dogReadyState=getKernel().getState();
                }catch (Exception e){
                    log.info("kernel connect is interrupted");
                    setConnect(false);
                    kernel=null;
                    connect();//reconnect
                }
            }
        }, 5000, 5000);
    }

    private synchronized Boolean getKernelManualClose() {return kernelCloseManual;}

    private synchronized void setKernelManualClose(Boolean close) { kernelCloseManual = close;}

    private KernelServicePortal getKernel() { return kernel;}





    public String getProcState(String vehiclename) {
        String procstate="N/A";
        try {
            if(getKernel()!=null && this.connect) {
                VehicleService vservice = kernel.getVehicleService();
                Vehicle vehicle = vservice.fetchObject(Vehicle.class, vehiclename);
                if (vehicle.getProcState() != null){
                    procstate = vehicle.getProcState().name();
                }
            }
        }catch (Exception e){
            log.info("error is at getProcState {}",e);
            e.printStackTrace();
        }
        return procstate;
    }

    public String getVehicleStatus(String vehiclename) {
        String vs="N/A";
        try {
            if(getKernel()!=null && this.connect) {
                VehicleService vservice = kernel.getVehicleService();
                Vehicle vehicle = vservice.fetchObject(Vehicle.class, vehiclename);
                vs = vehicle.getState().name();
            }
        }catch (Exception e){
            log.info("error is at getVehicleStatus {}",e);
            e.printStackTrace();
        }
        return vs;
    }

    //获得error1信息(从AGV车中获取相关的错误信息)
    public int getError(String vehicleName,String errorName) {
        int error = 0;
        try {
            if(getKernel()!=null && this.connect) {
                VehicleService vservice = kernel.getVehicleService();
                Vehicle vehicle = vservice.fetchObject(Vehicle.class, vehicleName);
                error = Integer.valueOf(vehicle.getProperty(errorName));
            }
        } catch (Exception e) {
            log.info("error is at getError {}",e);
            e.printStackTrace();
        }
        return error;
    }

    public void CreateOrderOneStation(String origin1, String target1, String orderName) {
        try {
            if(getKernel()!=null && this.connect) {
                TransportOrderService orderService = kernel.getTransportOrderService();//获得订单服务
                DispatcherService dispatcherService = kernel.getDispatcherService();//获得调度服务
                List<DestinationCreationTO> destinations = new LinkedList();//此数组用来获得调度的数据
                //source，dest，sa，da是调度的数据
                String source = "Location-" + origin1;
                String dest = "Location-" + target1;
                String sa = "Load cargo:00";//此处进行动作拆分(仿佛有一个动作的序列)
                String da = "Unload cargo:00";// 卸货动作*/
                //将调度的数据放至数组
                destinations.add(new DestinationCreationTO(source, sa));
                destinations.add(new DestinationCreationTO(dest, da));
                //orderTO为调度任务的数据(包括任务号以及起始目的数据)
                TransportOrderCreationTO orderTO = new TransportOrderCreationTO(orderName, destinations);
                //为调度任务的属性,将托盘号以及其它的放在任务里面(将数据信息放在调度任务里,以后可以从这个任务获取到相关的信息)
                orderTO = orderTO.withProperty("start", origin1);
                orderTO = orderTO.withProperty("end", target1);
                orderTO = orderTO.withProperty("orderName", orderName);
                TransportOrder TO = orderService.createTransportOrder(orderTO);//创建调度任务
                dispatcherService.dispatch();//调度该任务
                log.info("task create success---orderName={},start={},end={}", orderName, origin1, target1);
            }
        }catch (Exception e){
            log.info("error is at CreateOrderOneStation {}",e);
            e.printStackTrace();
        }
    }

    public String getinter(String vehiclename) {//获得该车的调用级别
        String level = "N/A";
        try {
            if(getKernel()!=null && this.connect) {
                VehicleService vservice = kernel.getVehicleService();
                Vehicle vehicle = vservice.fetchObject(Vehicle.class, vehiclename);
                if (vehicle.getIntegrationLevel() != null){
                    level = vehicle.getIntegrationLevel().toString();
                }
            }
        } catch (Exception e) {
            log.info("error is at getinter {}",e);
            e.printStackTrace();
        }
        return level;
    }
}
